﻿using System;
using System.Linq;
using NLite.Threading;
using System.Threading;

namespace NLite.Messaging.Internal
{
    [Serializable]
    class Executor : IExecutor
    {
        public ISubject Subject { get; set; }
        public IMessageListenerManager ListnerManager { get; set; }
        public IDelegateInvoker DelegateInvoker { get; set; }

        public void Execute()
        {
            var handlers = Subject.Subscriber.Observers;
            if (handlers != null && handlers.Count > 0)
                DoExecute();
        }

       protected virtual void DoExecute()
        {
            if (Subject.Queue.Count == 0)
                return;

            var pair = Subject.Queue.Dequeue();
            var sender = pair.Key;
            var message = pair.Value;

            var e = new MessageSendingEventArgs (sender,message);

            ListnerManager.OnSending(e);

            if (e.Canceled)
            {
                ListnerManager.OnSent(e);
                return;
            }

            DoExecute(sender, message);
            ListnerManager.OnSent(e);
        }



        protected virtual void DoExecute(object sender, IMessage message)
        {
            var handlers = Subject.Subscriber.Observers;

            if (ListnerManager.Where(item => (item.Type & MessageListnerType.Receiving) == MessageListnerType.Receiving).Count() == 0)
            {
                foreach (var item in handlers)
                {
                    if (!DoExecute(message, sender, item.Key))
                        break;
                }
            }
            else
            {

                var syncHandlers = handlers.Where(i => i.Value == SubscribeMode.Sync).Select(i => i.Key).ToArray();
                var asyncHandlers = handlers.Where(i => i.Value == SubscribeMode.Async).Select(i => i.Key).ToArray();

                foreach (var item in syncHandlers)
                {
                    if (!DoExecute(message, sender, item))
                        break;
                }


                Paraller.ForEach(asyncHandlers, handler => DoExecute(message, sender, handler));
            }
        }

        protected bool DoExecute(IMessage message,  object sender, IObserverHandler<IMessage> handle)
        {
            var e = new MessageReceivingEventArgs (sender,message, handle.Target);
            try
            {
                ListnerManager.OnReceiving(e);
                if(!e.Ignored)
                    DelegateInvoker.Invoke<IMessage>(handle, sender, message);
                    
                return true;
            }
            catch (Exception ex)
            {
                var re = new MessageExceptionEventArgs (sender, message,ex);
                ListnerManager.OnReceivingException(re);
                return !re.Canceled; 
            }
            finally
            {
                ListnerManager.OnReceived(new MessageEventArgs (sender, message));
                e = null;
            }
        }

    }
}
